<?php
/**
 * this file has one class Services_Douban
 *
 * PHP version 5
 *
 * @category Web_Services
 * @package  Services_Douban
 * @author   sink <sink.cup@gmail.com>
 * @license  http://opensource.org/licenses/bsd-license.php New BSD License
 * @link     http://pear.php.net/package/Services_Douban/
 */

require_once 'Douban/Exception.php';

/**
 * need HTTP_Request2 package
 */
require_once 'HTTP/Request2.php';

/**
 * php lib for douban api v2
 * douban.com is a chinese web site.
 * 对豆瓣apiv2进行的封装。
 * 豆瓣是一个汉字网站，主要用户为汉字用户。
 *
 * 豆瓣官方文档:
 * http://developers.douban.com/wiki/?title=api_v2
 *
 * @category Web_Services
 * @package  Services_Douban
 * @author   sink <sink.cup@gmail.com>
 * @license  http://opensource.org/licenses/bsd-license.php New BSD License
 * @link     http://pear.php.net/package/Services_Douban/
 */
class Services_Douban
{
    /**
     * Configuration parameters，豆瓣 api的地址，不用修改。
     * @var  array
     * @see  setConfig()
     */
    protected $api_conf = array(
        'get_me'         => 'https://api.douban.com/v2/user/~me',
        'get_movie'         => 'https://api.douban.com/v2/movie/',
        'get_token'         => 'https://www.douban.com/service/auth2/token',
    );

    /**
     * app conf
     * @var array $app_conf 在 http://developers.douban.com/apikey/
     * 看到的api_key和secret
     * 比如 array(
     *          'api_key' => 'foo',
     *          'secret'  => 'bar',
     *          'redirect_uri' => 'http://example.com',
     *      )
     */
    protected $app_conf;

    /**
     * 格式
     * 如果定义了格式，则对豆瓣api的返回值进行修改，再提供给调用者。
     */
    protected $format_conf = array(
        'movie' => array(
        ),
        'token' => array(
        ),
        'user' => array(
        ),
    );

    /**
     * HTTP lib
     */
    protected $http;

    /**
     * @var array $user 当前授权用户的token
     * 比如 array(
     *          'access_token'  => xxx,
     *          'refresh_token' => ooo,
     *      )
     */
    protected $user;

    /**
     * 构造
     *
     * @param array $app_conf    app配置，请看上面的成员变量注释
     * @param array $user        实例化时可以不传user，比如未登录状态调用登录接口
     * @param array $format_conf 可以不传
     */
    public function __construct($app_conf, $user = array(), $format_conf = array())
    {
        $this->app_conf = $app_conf;
        $this->http     = new HTTP_Request2();
        $this->setUser($user);
        $this->setFormatConf($format_conf);
    }
    
    /**
     * 对数据进行格式化
     *
     * @param array $data   数据
     * @param array $format 格式
     *
     * @return array
     */
    private function _format($data, $format)
    {
        if (empty($format)) {
            return $data;
        }
        foreach ($format as $k=>$v) {
            $data[$v] = $data[$k];
            unset($data[$k]);
        }
        return $data;
    }

    /**
     * 获取当前授权用户信息
     * 需要登录：是
     * http://developers.douban.com/wiki/?title=user_v2#get_me
     *
     * @return array 比如 array(
     *      'loc_id' => '108296',
     *      'name' => '已注销',
     *      'created' => '2006-10-05 15:47:27',
     *      'loc_name' => '上海',
     *      'avatar' => 'http://img3.douban.com/icon/u1274827-2.jpg',
     *      'uid' => 'blackegg',
     *      'alt' => 'http://www.douban.com/people/blackegg/',
     *      'id' => '1274827',
     *      'desc' => 'CCN初恋寻初恋，寻爱看电影的媳妇一枚，准备买房啦^o^',
     *  )
     * @throws Services_Douban_Exception
     */
    public function getMe()
    {
        $this->http->setUrl($this->api_conf['get_me']);
        $this->http->setMethod(HTTP_Request2::METHOD_GET);
        try {
            $r = $this->http->send();
            $tmp = json_decode($r->getBody(), true);
        } catch (Exception $e) {
            throw new Services_Douban_Exception('http error');
        }
        if (isset($tmp['id'])) {
            return $this->_format($tmp, $this->format_conf['user']);
        } else {
            throw new Services_Douban_Exception($tmp['msg'], $tmp['code']);
        }
    }


    /**
     * 获取电影信息
     * 需要登录：否
     * http://developers.douban.com/wiki/?title=movie_v2&_r=280929636176372089#get_movie
     *
     * @param string $id 豆瓣电影id
     *
     * @return array 比如 array (
     *     'rating' => 
     *     array (
     *       'max' => 10,
     *       'average' => '8.9',
     *       'numRaters' => 172096,
     *       'min' => 0,
     *     ),
     *     'author' => 
     *     array (
     *       0 => 
     *       array (
     *         'name' => '刘镇伟',
     *       ),
     *     ),
     *     'alt_title' => '大话西游之大圣娶亲',
     *     'image' => 'http://img3.douban.com/spic/s1311495.jpg',
     *     'title' => '西游记大结局之仙履奇缘',
     *     'mobile_link' => 'http://m.douban.com/movie/subject/1292213/',
     *     'summary' => '至尊宝（周星驰）被月光宝盒带回到五百年前，遇见紫霞仙子（朱茵），被对方打上烙印成为对方的人，并发觉自己已变成孙悟空。
     *   紫霞与青霞（朱茵）本是如来佛祖座前日月神灯的灯芯（白天是紫霞，晚上是青霞），二人虽然同一肉身却仇恨颇深，因此紫霞立下誓言，谁能拔出她手中的紫青宝剑，谁就是她的意中人。紫青宝剑被至尊宝于不经意间拔出，紫霞决定以身相许，却遭一心记挂白晶晶（莫文蔚）的至尊宝拒绝。后牛魔王救下迷失在沙漠中的紫霞，并逼紫霞与他成婚，关键时刻，至尊宝现身。',
     *     'attrs' => 
     *     array (
     *       'pubdate' => 
     *       array (
     *         0 => '1995-02-04(香港)',
     *       ),
     *       'language' => 
     *       array (
     *         0 => '粤语',
     *         1 => '普通话',
     *       ),
     *       'title' => 
     *       array (
     *         0 => '西游记大结局之仙履奇缘',
     *       ),
     *       'country' => 
     *       array (
     *         0 => '中国大陆',
     *         1 => '香港',
     *       ),
     *       'writer' => 
     *       array (
     *         0 => '刘镇伟 Jeffrey Lau',
     *       ),
     *       'director' => 
     *       array (
     *         0 => '刘镇伟',
     *       ),
     *       'cast' => 
     *       array (
     *         0 => '周星驰 Stephen Chow',
     *         1 => '朱茵 Athena Chu',
     *         2 => '莫文蔚 Karen Mok',
     *         3 => '吴孟达 Man Tat Ng',
     *         4 => '罗家英 Kar-Ying Law',
     *         5 => '蔡少芬 Ada Choi',
     *         6 => '蓝洁瑛 Kit Ying Lam',
     *         7 => '刘镇伟 Jeffrey Lau',
     *       ),
     *       'movie_duration' => 
     *       array (
     *         0 => '95分钟',
     *       ),
     *       'year' => 
     *       array (
     *         0 => '1994',
     *       ),
     *       'movie_type' => 
     *       array (
     *         0 => '动作',
     *         1 => '冒险',
     *         2 => '喜剧',
     *         3 => '奇幻',
     *         4 => '爱情',
     *       ),
     *     ),
     *     'alt' => 'http://movie.douban.com/movie/1292213',
     *     'id' => 'http://api.douban.com/movie/1292213',
     *     'tags' => 
     *     array (
     *       0 => 
     *       array (
     *         'count' => 23824,
     *         'name' => '喜剧',
     *       ),
     *       1 => 
     *       array (
     *         'count' => 22629,
     *         'name' => '经典',
     *       ),
     *       2 => 
     *       array (
     *         'count' => 17311,
     *         'name' => '香港',
     *       ),
     *     ),
     *  )
     * @throws Services_Douban_Exception
     */
    public function getMovie($id)
    {
        $this->http->setUrl($this->api_conf['get_movie'] . $id);
        $this->http->setMethod(HTTP_Request2::METHOD_GET);
        try {
            $r = $this->http->send();
            $data = json_decode($r->getBody(), true);
        } catch (Exception $e) {
            throw new Services_Douban_Exception('http error');
        }
        if (isset($data['title'])) {
            //发布时间
            if (!isset($data['attrs']['year'][0]) && isset($data['attrs']['pubdate'][0])) {
                $data['attrs']['year'][0] = substr($data['attrs']['pubdate'][0], 0, 4);
            }
            //如果不是中国大陆，则title不是汉字，到alt_title里截取汉字
            //大部分电影都是中国大陆，但十月围城 的结果 是 中国，而不是中国大陆
            //破事儿 是 香港
            //艋舺 Monga 是 台湾
            $tmp = array_intersect(array('中国大陆', '中国', '香港'), $data['attrs']['country']);
            if (empty($tmp)) {
                $tmp = explode('/', $data['alt_title']);
                if(isset($tmp[0]) && !empty($tmp[0])) {
                    //把外文的title保存到alt_title中
                    $data['alt_title'] = str_replace($tmp[0], $data['title'], $data['alt_title']);
                    $data['title'] = trim($tmp[0]);
                }
            }
            return $this->_format($data, $this->format_conf['movie']);
        } else {
            throw new Services_Douban_Exception($data['msg'], $data['code']);
        }
    }

    /**
     * 需要登录：否
     * 获取 access_token, refresh_token
     *
     * @param string $code 跳回redirect_uri时带的code数据
     *
     * @return array 比如 array(
     *          'access_token'  => xxx,
     *          'refresh_token' => ooo,
     *      )
     * @throws Services_Douban_Exception
     */
    public function getToken($code)
    {
        $this->http->setUrl($this->api_conf['get_token']);
        $this->http->setMethod(HTTP_Request2::METHOD_POST);
        $data = array(
            'client_id' => $this->app_conf['api_key'],
            'client_secret' => $this->app_conf['secret'],
            'redirect_uri' => $this->app_conf['redirect_uri'],
            'grant_type' => 'authorization_code',
            'code' => $code,
        );
        $this->http->addPostParameter($data);
        try {
            $r = $this->http->send();
            $tmp = json_decode($r->getBody(), true);
        } catch (Exception $e) {
            throw new Services_Douban_Exception('http error');
        }
        if (isset($tmp['access_token'])) {
            return $this->_format($tmp, $this->format_conf['token']);
        } else {
            throw new Services_Douban_Exception($tmp['msg'], $tmp['code']);
        }
    }

    /**
     * 设置返回格式
     *
     * @param array $format_conf 格式
     *
     * @return bool
     */
    public function setFormatConf($format_conf)
    {
        if (empty($format_conf)) {
            return true;
        }
        foreach ($format_conf as $k=>$v) {
            $this->format_conf[$k] = $v;
        }
        return true;
    }

    /**
     * 为请求加上用户信息
     * 有些接口需要用户先登录授权，授权后，每次请求在http header中带着token即可。
     * Header格式为"Authorization: Bearer access_token"，其中Bearer后面有一个空格 
     * http://developers.douban.com/wiki/?title=connect
     *
     * @param array $user 用户信息
     *
     * @return bool
     *
     */
    public function setUser($user)
    {
        $this->user = $user;
        if (isset($this->user['access_token'])) {
            $header = array(
                'Authorization' => 'Bearer ' . $this->user['access_token'],
            );
            $this->http->setHeader($header);
        }
        return true;
    }
}
?>
